<?php

namespace App\Console\Commands\Command;

use App\Models\Train\Stations\Repositories\Interfaces\StationRepositoryInterface;
use App\Models\Train\Stations\Repositories\StationRepository;
use App\Trip\Geo\Amap\POI;
use Exception;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Maatwebsite\Excel\Facades\Excel;

class GenerateTrainStationMap
{

    /**
     * @var StationRepository $stationRepo
     */
    private mixed      $stationRepo;
    private array      $fixedDatas;
    private Collection $stations;
    private POI        $poi;
    private string     $br;
    private bool       $forceMatch;

    public function __construct()
    {
        $this->poi          = new POI();
        $this->stationRepo  = app(StationRepositoryInterface::class);
        $fixed_station_file = storage_path('train/fixed_stations.xlsx');
        $maps               = [];
        $collection         = new Collection();
        try {
            $data = Excel::toArray($collection, $fixed_station_file);
        } catch (Exception $e) {
            $data = [];
        }
        $fixedData = $data[0] ?? [];
        $fixedData && array_shift($fixedData);
        foreach ($fixedData as $fixed) {
            $maps[$fixed[0]] = [
                "station_name" => $fixed[0],
                "province"     => $fixed[1],
                "city"         => $fixed[2],
                "district"     => $fixed[3],
                "address"      => $fixed[4],
            ];
        }
        $this->fixedDatas = $maps;
        $this->forceMatch = false;
        $this->stations   = $this->stationRepo->listStations();
        $this->br         = "\n";
    }

    /**
     * 是否强制匹配
     * @return $this
     */
    public function setForceMatch(): static
    {
        $this->forceMatch = true;
        return $this;
    }

    /**
     * 覆盖需要更新的站点
     * @param Collection $stations
     * @return $this
     */
    public function setStations(Collection $stations): static
    {
        $this->stations = $stations;
        return $this;
    }

    /**
     * @throws GuzzleException
     */
    public function matchStationCity(): void
    {
        $count = count($this->stations);
        echo "站点匹配城市:共计{$count}{$this->br}";
        foreach ($this->stations as $key => $station) {
            if (!$this->isStationMatch($station)) {
                continue;
            }

            // 匹配站点通过文件
//            if ($this->isFixStation($station->station_name)) {
//                $isFixed = $this->matchByFixedData($station);
//                if ($isFixed) {
//                    continue;
//                }
//            }

            $city           = '';
            $types          = "150200|150210";
            $stationAreaCfg = config("train_station.station_area");
            if (isset($stationAreaCfg[$station->station_code])) {
                $city  = $stationAreaCfg[$station->station_code]['citycode'];
                $types = $stationAreaCfg[$station->station_code]['typecode'];
            }
            $zhan_station_name = Str::contains($station->station_name, "站") ? $station->station_name : $station->station_name."站";
            $levels            = [
                [
                    "keywords" => "{$this->dealQueryStationName($zhan_station_name)}",
                    "types"    => $types,
                    "city"     => $city,
                ],
                [
                    "keywords" => "{$this->dealQueryStationName($station->station_name)}",
                    "types"    => $types,
                    "city"     => $city,
                ],
            ];

            $matchRes        = false;
            $merge_positions = [];
            $names           = [];
            foreach ($levels as $level) {
                $positions                 = $this->poi->searchByKeywords(
                    $level["keywords"],
                    $level["types"],
                    $level["city"],
                    5
                );
                $names[$level["keywords"]] = $level["keywords"];
                if (collect($positions)->where('name', $level["keywords"])->count() > 0) {
                    $merge_positions = $positions;
                    break;
                }
                $merge_positions = array_merge($merge_positions, $positions);
            }

            // 优先提取与之匹配的结果
            $positions = collect($merge_positions)->unique('name')->sortByDesc(function ($v) use ($names) {
                if (in_array($v['name'], $names)) {
                    return 100;
                } else {
                    return 1;
                }
            })->toArray();

            $match = function ($station, $positions) use ($level) {
                foreach ($positions as $position) {
                    $isMatch = Str::contains($this->dealTypeCode2Str($position['typecode']), ['150200', '150210']);
                    // 如果匹配到火车站或货车站
                    if ($isMatch) {
                        // 判断返回的 name 是否和 站点名称相似
                        if (!$this->similarStr($position['name'], $level["keywords"])) {
                            echo "{$station->station_name}-{$station->station_code}:匹配1-{$position['name']}-不含站点名称kw-{$level["keywords"]}-请自行甄别{$this->br}";
                            continue;
                        }
                        $this->addStation($station, $position);
                        return true;
                    }
                }
                return false;
            };

            $matched = $match($station, $positions);
            if ($matched) {
                $matchRes = true;
            }


            if (!$matchRes) {
                if ($this->isFixStation($station->station_name)) {
                    $isFixed = $this->matchByFixedData($station);
                    if (!$isFixed) {
                        echo "{$station->station_name}-{$station->station_code}-匹配失败清空数据-{$this->br}";
                        $this->matchFailedAndClearData($station);
                    }
                }
            }
        }
    }

    /**
     * 站点是否匹配
     * @param $station
     * @return bool true 匹配 false 不匹配
     */
    private function isStationMatch($station): bool
    {
        // 强制匹配
        if ($this->forceMatch) {
            return true;
        }
        // 已匹配的 false
        if ($station->match_state == 1) {
            return false;
        }
        return true;
    }

    private function dealQueryStationName(string $station_name): string
    {
        return Str::replace(" ", "", $station_name);
    }

    private function dealTypeCode2Str(mixed $typecode): string
    {
        return is_string($typecode) ? $typecode : json_encode($typecode, JSON_UNESCAPED_UNICODE);
    }

    /**
     *  两个字符串是否相似 (a包含b || b包含a ) 为真
     * @param string $str1
     * @param string $str2
     * @return bool
     */
    private function similarStr(string $str1, string $str2): bool
    {
        // 先去除空格
        $str1 = Str::replace(" ", "", $str1);
        $str2 = Str::replace(" ", "", $str2);
        return Str::contains($str1, $str2) || Str::contains($str2, $str1);
    }

    private function addStation($station, $position): void
    {
        $typecode = $this->dealTypeCode2Str($position['typecode']);
        $data     = [
            'pcode'          => $position['pcode'],
            'citycode'       => $position['citycode'],
            'adcode'         => $position['adcode'],
            'pname'          => $position['pname'],
            'cityname'       => $position['cityname'],
            'adname'         => $position['adname'],
            'location'       => $position['location'],
            'enter_location' => $position['entr_location'],
            'tel'            => $position['tel'],
            'poi_id'         => $position['id'],
            'navi_id'        => $position['navi_poiid'],
            'address'        => $position['address'],
            'state'          => 0,// 正常
            'amap_name'      => $position['name'],
            'match_type'     => $typecode,
            'match_state'    => 1,                                                      // 已匹配
            'is_stop'        => Str::contains($position['name'], '暂停营业') ? 1 : 0,       // 1 已停运
            'is_freight'     => Str::contains($typecode, '150210') ? 1 : 0,             // 1 货运
            'is_official'    => 0,
        ];
        $this->stationRepo->updateStation($station, $data);
    }

    /**
     * 站点是否是手动 补录的
     * @param string $station_name
     * @return bool
     */
    private function isFixStation(string $station_name): bool
    {
        return isset($this->fixedDatas[$station_name]);
    }

    /**
     * 用excel 修正的数据匹配
     * @param $station
     * @return bool
     * @throws GuzzleException
     */
    private function matchByFixedData($station): bool
    {
        $isMatch = false;
        // 优先等级
        $station_name = $this->dealQueryStationName($station->station_name);
        $fixedData    = $this->getFixStation($station->station_name);
        /**
         * 0 "station_name"
         * 1 "province"
         * 2 "city"
         * 3 "district"
         * 4 "address"
         */
        $city   = $fixedData["city"];
        $types  = "150200|150210";
        $levels = [
            [
                "keywords" => "{$this->dealQueryStationName($station_name)}站",
                "types"    => $types,
                "city"     => $city,
            ],
            [
                "keywords" => "{$this->dealQueryStationName($station_name)}",
                "types"    => $types,
                "city"     => $city,
            ],
            [
                "keywords" => ($fixedData['district'] ?? '')."{$this->dealQueryStationName($station_name)}",
                "types"    => "",
                "city"     => $city,
            ],
            [
                "keywords" => "{$this->dealQueryStationName($station_name)}",
                "types"    => $types,
                "city"     => $city.($fixedData['district'] ?? ''),
            ],
            [
                "keywords" => "{$this->dealQueryStationName($station_name)}".($fixedData['address'] ?? ''),
                "types"    => "",
                "city"     => $city,
            ],
        ];

        $match = function ($level) use ($station, $fixedData, $station_name) {
            $positions = $this->poi->searchByKeywords(
                $level["keywords"],
                $level["types"],
                $level["city"],
                1
            );

            foreach ($positions as $position) {
                if (!$position['pname'] || !$position['cityname']) {
                    continue;
                }
                try {
                    if (!($fixedData['province'] == $position['pname'] || Str::limit($fixedData['province'], 2) == Str::limit($position['pname'], 2))) {
                        echo "{$station->station_name}-{$station->station_code}-理论{$fixedData['province']}-实际-{$position['pname']}-省份不正确,已跳过{$this->br}";
                        continue;
                    }

                    if (!($fixedData['city'] == $position['cityname'] || Str::limit($fixedData['city'], 4) == Str::limit($position['cityname'], 4))) {
                        echo "{$station->station_name}-{$station->station_code}-理论{$fixedData['city']}-实际-{$position['cityname']}-城市不正确,已跳过{$this->br}";
                        continue;
                    }

                    // 判断返回的 name 是否和 站点名称相似
                    if (!$this->similarStr($position['name'], $station_name)) {
                        echo "{$station->station_name}-{$station->station_code}:匹配2-{$position['name']}-不含站点名称-{$station_name}-请自行甄别{$this->br}";
//                        continue;
                    }
                    $this->addStation($station, $position);
                    return true;
                } catch (Exception $e) {
                    echo "{$station->station_name}-{$station->station_code}:数据异常{$e->getMessage()}--{{$e->getLine()}}{$this->br}";
                }
            }
            return false;
        };
        foreach ($levels as $level) {
            $matched = $match($level);
            if ($matched) {
                $isMatch = true;
                break;
            }
        }
        return $isMatch;
    }

    /**
     * 获取手动补录的站点数据
     * @param string $station_name
     * @return array
     */
    private function getFixStation(string $station_name): array
    {
        return $this->fixedDatas[$station_name] ?? [];
    }

    /**
     * 匹配失败
     * @param $station
     * @return void
     */
    private function matchFailedAndClearData($station): void
    {
        $emptyData = [
            'pcode'          => '',
            'citycode'       => '',
            'adcode'         => '',
            'pname'          => '',
            'cityname'       => '',
            'adname'         => '',
            'location'       => '',
            'enter_location' => '',
            'tel'            => '',
            'poi_id'         => '',
            'navi_id'        => '',
            'address'        => '',
            'state'          => 0,
            'amap_name'      => '',
            'match_type'     => '',
            'match_state'    => 2, // 匹配异常
            'is_stop'        => 0,
            'is_freight'     => 0,
            'is_official'    => 0,
        ];
        $this->stationRepo->updateStation($station, $emptyData);

    }
}